#include "image.h"
#include "external/pHash-0.9.4/pHash.h"
#include "external/blockhash/blockhash.h"

namespace renderit
{
	namespace image
	{
		image::image()
			: colors_(nullptr)
			, width_(0)
			, height_(0)
			, block_hash_(nullptr)
		{

		}

		image::~image()
		{
			if (colors_)
			{
				delete[] colors_;
				colors_ = nullptr;
			}
			if (block_hash_)
			{
				free(block_hash_);
				block_hash_ = nullptr;
			}
		}

		void image::release()
		{
			if (colors_)
			{
				delete[] colors_;
				colors_ = nullptr;
				width_ = height_ = 0;
			}
		}

		void image::create(uint32 width, uint32 height)
		{
			if (colors_)
			{
				delete[] colors_;
			}

			colors_ = new core::color4b[width * height];
			width_ = width;
			height_ = height;
		}

		const int* image::get_blockhash()
		{
			if (block_hash_)
			{
				return block_hash_;
			}

			blockhash(BLOCK_HASH_BLOCKS, (unsigned char *)colors_, width_, height_, &block_hash_);

			return block_hash_;
		}

		static uint32 hamming_distance(const uint64& a, const uint64& b)
		{
			uint64 x = a ^ b;
			const uint64 m1 = 0x5555555555555555ULL;
			const uint64 m2 = 0x3333333333333333ULL;
			const uint64 h01 = 0x0101010101010101ULL;
			const uint64 m4 = 0x0f0f0f0f0f0f0f0fULL;
			x -= (x >> 1) & m1;
			x = (x & m2) + ((x >> 2) & m2);
			x = (x + (x >> 4)) & m4;
			return (x * h01) >> 56;
		}

		uint32 hamming_distance(const image * a, const image * b)
		{
			const int * ha = const_cast<image*>(a)->get_blockhash();
			const int * hb = const_cast<image*>(b)->get_blockhash();

			uint32 count = 0;
			for (int i = 0; i < BLOCK_HASH_BITS; ++i)
			{
				if (ha[i] != hb[i])
				{
					count++;
				}
			}
			return count;
		}
	}
}